﻿//===============================================================================
// Microsoft Innovation Centre - Western Europe
// Copyright (c) 2008 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Microsoft.WVB.UI
{
    public enum ElementLayout
    {
        Automatic,
        Manuel
    }

    public class RadialPanel : Panel
    {
        // Fields
        public static readonly DependencyProperty RadiusProperty = DependencyProperty.RegisterAttached("Radius", typeof(double), typeof(RadialPanel), null);
        public static readonly DependencyProperty AngleProperty = DependencyProperty.RegisterAttached("Angle", typeof(double), typeof(RadialPanel), null);
        public static readonly DependencyProperty AngleOffsetProperty = DependencyProperty.RegisterAttached("AngleOffset", typeof(double), typeof(RadialPanel), new PropertyMetadata(OnAngleOffsetChanged));
        public static readonly DependencyProperty ElementLayoutProperty = DependencyProperty.RegisterAttached("ElementLayout", typeof(ElementLayout), typeof(RadialPanel), null);

        private Size panelSize;
        private Point Centre = new Point(0, 0);
        private Point Radius = new Point(0, 0);

        //Methods
        protected static void OnAngleOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            RadialPanel rp = o as RadialPanel;
            if (rp != null)
            {
                rp.InvalidateArrange();
            }
        }

        public RadialPanel()
            : base()
        {

            //this.MouseMove += new MouseEventHandler(CarouselPanel_MouseMove);
            //this.MouseEnter += new MouseEventHandler(CarouselPanel_MouseEnter);
            //this.MouseLeave += new MouseEventHandler(CarouselPanel_MouseLeave);
            this.Loaded += new RoutedEventHandler(RadialPanel_Loaded);

        }

        void RadialPanel_Loaded(object sender, RoutedEventArgs e)
        {
            if (Children.Count > 0)
            {
                //ElementLayout el = (ElementLayout)this.GetValue(ElementLayoutProperty);
                ElementLayout el = (ElementLayout)(sender as RadialPanel).GetValue(ElementLayoutProperty);
                if (el == ElementLayout.Automatic)
                {
                    //Set default offset for automatic layout to 270
                    (this as RadialPanel).SetValue(RadialPanel.AngleOffsetProperty, 270.0);

                    Button b = null;
                    //double d = 0;
                    for (int i = 0; i < Children.Count; i++)
                    {
                        //Hack to get to the button inside the ContentPresenter
                        b = (Button)VisualTreeHelper.GetChild(Children[i], 0);
                        //d = (double)b.GetValue(RadialPanel.AngleProperty);
                        b.SetValue(RadialPanel.AngleProperty, (double)(i * (360 / Children.Count)));
                        //d = (double)b.GetValue(RadialPanel.AngleProperty);
                    }
                    this.InvalidateArrange();
                }
            }
            InitializeStoryboard();
        }


        /// <summary>   
        /// Sets the distance.   
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="radius">The distance.</param>
        public static void SetRadius(UIElement element, double radius)
        {
            element.SetValue(RadiusProperty, radius);
        }
        /// <summary>
        /// Gets the distance.  
        /// </summary>  
        /// <param name="element">The element.</param>
        /// <returns></returns>  
        public static double GetRadius(UIElement element)
        {
            return (double)element.GetValue(RadiusProperty);
        }

        /// <summary>
        /// Sets the angle.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="distance">The distance.</param>
        public static void SetAngle(UIElement element, double angle)
        {
            element.SetValue(AngleProperty, angle);
            element.InvalidateArrange();
        }

        /// <summary>
        /// Gets the angle.  
        /// </summary>  
        /// <param name="element">The element.</param>  
        /// <returns></returns>  
        public static double GetAngle(UIElement element)
        { return (double)element.GetValue(AngleProperty); }



        /// <summary>
        /// Sets the angle offset.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="distance">The offset.</param>
        public static void SetAngleOffset(UIElement element, double offset)
        {
            element.SetValue(AngleOffsetProperty, offset);
            element.InvalidateArrange();
        }

        /// <summary>
        /// Gets the angle offset.  
        /// </summary>  
        /// <param name="element">The element.</param>  
        /// <returns></returns>  
        public static double GetAngleOffset(UIElement element)
        { return (double)element.GetValue(AngleOffsetProperty); }


        /// <summary>
        /// Sets the angle offset.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="layout">The element layout.</param>
        public static void SetElementLayout(UIElement element, ElementLayout layout)
        {
            element.SetValue(ElementLayoutProperty, layout);
            element.InvalidateArrange();
        }

        /// <summary>
        /// Gets the angle offset.  
        /// </summary>  
        /// <param name="element">The element.</param>  
        /// <returns></returns>  
        public static ElementLayout GetElementLayout(UIElement element)
        { return (ElementLayout)element.GetValue(ElementLayoutProperty); }


        /// <summary>
        /// When implemented in a derived class, provides the behavior for the "Arrange" pass of Silverlight layout.
        /// </summary>
        /// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>   
        /// <returns>The actual size used.</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            if (Children.Count == 0)
                return finalSize;

            //Calculate centre of the radial panel
            Centre.X = finalSize.Width / 2;
            Centre.Y = finalSize.Height / 2;

            ElementLayout el = (ElementLayout)this.GetValue(ElementLayoutProperty);
            switch (el)
            {
                case ElementLayout.Automatic:
                    double maxW = 0;
                    double maxH = 0;
                    foreach (UIElement elem in Children)
                    {
                        if (elem.DesiredSize.Width > maxW) maxW = elem.DesiredSize.Width;
                        if (elem.DesiredSize.Height > maxH) maxH = elem.DesiredSize.Height;
                    }

                    // Adjust radius depending on child element size
                    Radius.X = (finalSize.Width - maxW) / 2;
                    Radius.Y = (finalSize.Height - maxH) / 2;

                    //Button b = null;
                    foreach (UIElement elem in Children)
                    {
                        /// Get the angle where the elem is.. 
                        //b = (Button)VisualTreeHelper.GetChild(elem, 0);
                        double degrees = (double)VisualTreeHelper.GetChild(elem, 0).GetValue(RadialPanel.AngleProperty);
                        double angleOffset = (double)this.GetValue(RadialPanel.AngleOffsetProperty);
                        double radians = (degrees + angleOffset) * (Math.PI / 180);

                        //Calculate the point on the circle for the element
                        Point childPoint = new Point(Math.Cos(radians) * Radius.X, -Math.Sin(radians) * Radius.Y);

                        //Offsetting the point to the Avalable rectangular area which is FinalSize.
                        Point actualChildPoint = new Point(Centre.X + childPoint.X - (elem.DesiredSize.Width / 2), Centre.Y + childPoint.Y - (elem.DesiredSize.Height / 2));

                        // ZIndex trick to position items that are closer to the selected element in the front  
                        int zIndex = (int)((actualChildPoint.Y * Children.Count) / (Radius.Y * 2));
                        elem.SetValue(Canvas.ZIndexProperty, zIndex);

                        //Call Arrange method on the child element by giving the calculated point as the placementPoint.
                        elem.Arrange(new Rect(actualChildPoint.X, actualChildPoint.Y, elem.DesiredSize.Width, elem.DesiredSize.Height));
                        //(elem as Button).Content = "x: " + (actualChildPoint.X).ToString() + " y: " + (actualChildPoint.Y).ToString();
                    }
                    break;

                case ElementLayout.Manuel:

                    foreach (UIElement elem in Children)
                    {
                        /// Get the angle where the elem is.. 
                        double angle = (double)VisualTreeHelper.GetChild(elem, 0).GetValue(RadialPanel.AngleProperty);
                        double angleOffset = (double)VisualTreeHelper.GetChild(elem, 0).GetValue(RadialPanel.AngleOffsetProperty);
                        double radius = (double)VisualTreeHelper.GetChild(elem, 0).GetValue(RadialPanel.RadiusProperty);
                        double radians = (angle + angleOffset) * (Math.PI / 180);

                        //Calculate the point on the circle for the element
                        Point childPoint = new Point(Math.Cos(radians) * radius, -Math.Sin(radians) * radius);

                        //Offsetting the point to the Avalable rectangular area which is FinalSize.
                        Point actualChildPoint = new Point(Centre.X + childPoint.X - (elem.DesiredSize.Width / 2), Centre.Y + childPoint.Y - (elem.DesiredSize.Height / 2));

                        // ZIndex trick to position items that are closer to the selected element in the front  
                        //int zIndex = (int)((actualChildPoint.Y * Children.Count) / (radius * 2));
                        //elem.SetValue(Canvas.ZIndexProperty, zIndex);

                        //Call Arrange method on the child element by giving the calculated point as the placementPoint.
                        elem.Arrange(new Rect(actualChildPoint.X, actualChildPoint.Y, elem.DesiredSize.Width, elem.DesiredSize.Height));
                    }
                    break;
            }
            return finalSize;
        }


        protected override Size MeasureOverride(Size availableSize)
        {
            for (int i = 0; i < Children.Count; i++)
            {
                UIElement item = Children[i];
                //Give Infinite size as the avaiable size for all the children
                //item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                item.Measure(new Size(availableSize.Width, availableSize.Height));
            }
            panelSize = availableSize;
            return availableSize;
        }

        Storyboard sbSpin;
        DoubleAnimation daSpin;
        Boolean spinning = false;

        private void InitializeStoryboard()
        {
            if (sbSpin == null)
            {
                sbSpin = new Storyboard();
                sbSpin.SetValue(FrameworkElement.NameProperty, "sbAnimation");
                sbSpin.SetValue(Storyboard.TargetNameProperty, this.Name);
                sbSpin.Completed += new EventHandler(sbSpin_Completed);
                sbSpin.Duration = new Duration(TimeSpan.FromSeconds(0.5));

                daSpin = new DoubleAnimation();
                //dax.SetValue(FrameworkElement.NameProperty, "rpAnimation");
                daSpin.By = 45;
                daSpin.Duration = new Duration(TimeSpan.FromSeconds(0.5));
                //dax.SetValue(Storyboard.TargetNameProperty, "rp");
                daSpin.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(RadialPanel.AngleOffset)"));
                //daSpin.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(BroadCasterPanel.AngleOffset)"));
                sbSpin.Children.Add(daSpin);
                this.Resources.Add("rpResource", sbSpin);
            }
        }

        void sbSpin_Completed(object sender, EventArgs e)
        {
            spinning = false;
        }

        public void Spin(Button button, RadialPanel rp)
        {
            if (!spinning)
            {
                //sbSpin.Stop();
                spinning = true;
                string s = (string)button.GetValue(FrameworkElement.NameProperty);
                double a = (double)button.GetValue(RadialPanel.AngleProperty);
                double ao = (double)rp.GetValue(RadialPanel.AngleOffsetProperty);
                double c;
                double b;

                //Calculate button actual angle
                b = (a + ao) % 360;
                //Adjust if current offset is a negative value
                if (b < 0)
                    b = 360 + b;
                //Calculate new angle offset based on button pressed
                if (b >= 90 & b < 360)
                    c = 270 - b;
                else
                    c = -90 - b;

                //Assign new offset to storyboard animation
                if (c != daSpin.By)
                    daSpin.By = c;
                sbSpin.Begin();
            }
        }



    }
}
